package stendcontrol;

import java.util.ArrayList;
import services.ConvIntByte;

public class MessageBuilder
        implements TransmitListener, Runnable {

    /**
     * количество входных буферов
     */
    protected final int cnt_bufbuilder = 10;
    /**
     * длина буферов
     */
    protected final int len_bufbuilder = 10000;
    /**
     * количество измерений в последовательности измерений
     */
    protected final int len_measure_sequence =
            Configurator.MAX_SIZE_MEASURE_SEQUENCE;
    /**
     * список слушателей пакетов
     */
    protected ArrayList<MessageListener> msg_lstrs = new ArrayList<MessageListener>();
    /**
     * массив буферов
     */
    protected BufferBuilder[] buffer_builder =
            new BufferBuilder[cnt_bufbuilder];
    protected BufferBuilder buf_measure_sequence =
            new BufferBuilder(len_measure_sequence, Configurator.LONG_LEN_MSG);
    protected BufferBuilder statusMsgBuf = new BufferBuilder(len_bufbuilder, Configurator.SHORT_LEN_MSG);
    /**
     * номер текущего бефера
     */
    protected int cur_buffer = 0;
    /**
     * номер бефера на обработку
     */
    protected int process_buffer = 0;
    /**
     * метка заполнения текущего буфера
     */
    protected int cnt_in_curbuf = 0;
    /**
     * метка заполнения буфера последоватедьности
     */
    protected int cnt_in_measbuf = 0;
    /**
     * метка количества обработанных статусных сообщений.
     */
    protected int sts_msg_cnt = 0;
    /**
     * Последняя сгенерированная последовательность измерений
     */
    protected MeasureSeqMessage lastMeasureSeq;

    public MessageBuilder() {
        for (int i = 0; i < cnt_bufbuilder; i++) {
            buffer_builder[i] = new BufferBuilder(len_bufbuilder,
                    Configurator.LONG_LEN_MSG);
        }
    }

    /**
     * Operation
     *
     * @param msgListener
     * @return
     */
    public synchronized void addMessageListener(MessageListener msgListener) {
        msg_lstrs.add(msgListener);
    }

    /**
     * Operation
     *
     * @param msgListener
     * @return
     */
    public synchronized void removeMessageListener(MessageListener msgListener) {
        msg_lstrs.remove(msgListener);
    }

    /**
     * Operation
     *
     * @param n_msg_lstr
     * @return
     */
    public synchronized void removeMessageListener(int n_msg_lstr) {
        msg_lstrs.remove(n_msg_lstr);
    }

    /**
     * Удалить всех слушателей сообщений
     */
    public synchronized void removeAllMessageListener() {
        msg_lstrs.clear();
    }

    public synchronized void removeAllExperimentMessageListener() {
        for (MessageListener ml : msg_lstrs) {
            if (ml.getClass() == Experiment.class) {
                msg_lstrs.remove(ml);
            }
        }
    }

    /**
     * Operation
     *
     * @return long
     */
    public long getCountLostPackets() {
        return 0;
    }

    /**
     * Operation
     *
     * @return long
     */
    public long getCountPackets() {
        return 0;
    }

    @Override
    public void transmitPerformed(byte[] buf, int len) {
        /*  System.out.print("Buf :" );
         for (int i = 0; i < len; i++) {
         System.out.print(" " + Integer.toHexString(ConvIntByte.
         byte2Int(buf[i])));
         }
         ;*/

        //    System.out.println("Before if ");
        if (building) {
           
            if (buf[0] >> 4 == AbstractMessage.MSG_STS) {
                statusMsgBuf.add(buf, len);
                initGenStatusMsg();
                
            } else if (cnt_in_curbuf < len_bufbuilder) {
                buffer_builder[cur_buffer].add(buf, len);
                cnt_in_curbuf = buffer_builder[cur_buffer].getSize();
            } else {

                incCurBuffer();
                transmitPerformed(buf, len);
            }
        }
    }

    private synchronized void incCurBuffer() {
        if (++cur_buffer >= cnt_bufbuilder) {
            cur_buffer = 0;
        }
        if (cur_buffer == process_buffer) {
            StendLogger.log(StendLogger.MSG_ERROR, "Fatal error! "
                    + "all buffers in MessageBuilder overflow! "
                    + "Measures is losted! Need many buffers.");
        }
        buffer_builder[cur_buffer].removeAll();
        cnt_in_curbuf = 0;
        notify();
    }

    /**
     * Обработка заполненного буфера
     */
    private synchronized void processingBuffer() {
        //System.out.println("Processing bufer");
        while (cur_buffer == process_buffer && building) {
            try {
                wait();
            } catch (InterruptedException ex) {
                StendLogger.log(StendLogger.MSG_ERROR, "Error processed full"
                        + " bufer in MessageBuilder msg: " + ex.getMessage());
            }

            while (sts_msg_cnt != statusMsgBuf.getSize()) {
                transtalionToListenes
                        (genStatusMessage(statusMsgBuf.forcedGet(sts_msg_cnt)));
                sts_msg_cnt++;
                if (sts_msg_cnt == len_bufbuilder) {
                    sts_msg_cnt = 0;

                }
            }
        }
        byte[] lbuf;
        int lbuf_type;
        int cnt_measures = buffer_builder[process_buffer].getSize();
        for (int i = 0; i < cnt_measures; i++) {
            lbuf = buffer_builder[process_buffer].get(i);

            if (lbuf.length != 0) {
                lbuf_type = lbuf[0] >> 4;
                if (lbuf_type == AbstractMessage.MSG_MSEQ
                        && lbuf.length == Configurator.LONG_LEN_MSG) {
                    buf_measure_sequence.add(lbuf, lbuf.length);
                    if (buf_measure_sequence.getSize() == len_measure_sequence
                            || (!building && i == cnt_measures - 1)) {
                        transtalionToListenes(genMeasureSeqFromBuffer(
                                buf_measure_sequence));
                        buf_measure_sequence.removeAll();
                    }
                } else if (lbuf_type == AbstractMessage.MSG_STS
                        && lbuf.length == Configurator.SHORT_LEN_MSG) {
                    if (buf_measure_sequence.getSize() > 0) {
                        transtalionToListenes(genMeasureSeqFromBuffer(
                                buf_measure_sequence));
                        buf_measure_sequence.removeAll();
                    }
                    transtalionToListenes(genStatusMessage(lbuf));
                } else {
                    StendLogger.log(StendLogger.MSG_WARNING, "Unknow packet"
                            + "processing buffer");
                }
            } else {
                StendLogger.log(StendLogger.MSG_WARNING, "Empty packet in"
                        + "processing buffer");
            }
        }
        if (++process_buffer == cnt_bufbuilder) {
            process_buffer = 0;
        }
        if (!building) {
            clearAllData();
        }
    }

    private void clearAllData() {
        for (int i = 0; i < cnt_bufbuilder; i++) {
            buffer_builder[i].removeAll();
        }
        cur_buffer = 0;
        process_buffer = 0;
    }

    private StatusMessage genStatusMessage(byte[] buf) {
        if (buf.length >= Configurator.SHORT_LEN_MSG) {
          //  System.out.println("Status message: " + buf[0]);

            return new StatusMessage(ConvIntByte.byte2Int(buf[1]), 
                    DataExtract.getLongParam(buf, 2));
        } else {
            StendLogger.log(StendLogger.MSG_ERROR, "Small buffer for "
                    + "get StatusMessage. Length buf " + buf.length
                    + " min need length: 3");
            return null;
        }
    }

    private MeasureSeqMessage genMeasureSeqFromBuffer(BufferBuilder bbild) {
        MeasureSeqMessage msm = new MeasureSeqMessage(lastMeasureSeq);
        if (lastMeasureSeq != null) {
            lastMeasureSeq.setNextMeasureSeqMessage(msm);
        }
        lastMeasureSeq = msm;

        int[] sensors = new int[Configurator.COUNT_SENSOR];
        for (int i = 0; i < bbild.getSize(); i++) {
            for (int j = 0; j < sensors.length; j++) {
                sensors[j] = DataExtract.getSensor(bbild.get(i), j);
            }
            msm.addMeasure(sensors, DataExtract.getPosition(bbild.get(i)));
        }
        bbild.removeAll();
        return msm;
    }

    public float getFillLevel() {
        return process_buffer >= cur_buffer
                ? (process_buffer - cur_buffer) / (float)cnt_bufbuilder
                : (cnt_bufbuilder - (cur_buffer - process_buffer)) / (float)cnt_bufbuilder;
    }

    /*private MeasureSeqMessage genMeasureSeqFromBuffer(BufferBuilder bbild,
     int startpos, int endpos) {
     System.out.println("startpos:"+startpos+" endpos:"+endpos);
     MeasureSeqMessage msm = new MeasureSeqMessage(lastMeasureSeq);
     if (lastMeasureSeq != null) {
     lastMeasureSeq.setNextMeasureSeqMessage(msm);
     }
     lastMeasureSeq = msm;

     int[] sensors = new int[Configurator.COUNT_SENSOR];
     for (int i = startpos; i <= endpos; i++) {
     for (int j = 0; j < sensors.length; j++) {
     sensors[j] = getSensor(bbild.get(i), j);
     }
     msm.addMeasure(sensors, getPosition(bbild.get(i)));
     }
     if (endpos == len_bufbuilder - 1) {
     bbild.removeAll();
     }
     return msm;
     }*/
    private synchronized void transtalionToListenes(AbstractMessage amess) {
        int i = msg_lstrs.size();
        while (i-- != 0) {
            msg_lstrs.get(i).msgBuilded(amess);
        }
    }
    private boolean building = false;

    public synchronized void stopBuilding() {
        building = false;
        notify();
    }

    @Override
    public void run() {
        building = true;
        while (building) {
            processingBuffer();
        }
    }

    private synchronized void initGenStatusMsg() {
        notify();
    }
}
